use crate::ast::{Code, Expr, ExprKind, Stmt, Op};
use crate::lexer::Lexer;
use crate::token::TokenKind;

pub struct Parser<'a> {
    lexer: &'a mut Lexer<'a>,
}

impl<'a> Parser<'a> {
    pub fn new(lexer: &'a mut Lexer<'a>) -> Self {
        Parser {lexer}
    }


    fn parse_stmt(&mut self) -> Result<Stmt, String> {
        let expr = self.parse_expr()?;
        match expr.kind {
            ExprKind::EOF => Ok(Stmt::EOF),
            _ => Ok(Stmt::expr(expr)),
        }
    }

    fn parse_expr(&mut self) -> Result<Expr, String> {
        let mut peekable = self.lexer.peekable();
        let expr = match peekable.peek() {
            Some(Ok(token)) => {
                match token.kind {
                    TokenKind::Int(_) => Ok(Expr::single(token)),
                    TokenKind::Float(_) => Ok(Expr::single(token)),
                    TokenKind::Bool(_) => Ok(Expr::single(token)),
                    TokenKind::Str => Ok(Expr::single(token)),
                    TokenKind::EOF => Ok(Expr::eof(token)),
                    _ => Err(format!("Unexpected token: {}", token.text)),
                }
            }
            Some(Err(e)) => Err(e.to_string()),
            None => Err("Unexpected none token".to_string()),
        };
        peekable.next();
        if expr.is_err() {
            return expr;
        }
        // new line
        match peekable.peek() {
            Some(Ok(token)) => {
                match token.kind {
                    TokenKind::LF => (),
                    TokenKind::Add => {
                        let rhs = self.parse_expr()?;
                        let bina = Expr::binary(Op::Add, expr.unwrap(), rhs);
                        return Ok(bina);
                    },
                    TokenKind::Sub => {
                        let rhs = self.parse_expr()?;
                        let bina = Expr::binary(Op::Sub, expr.unwrap(), rhs);
                        return Ok(bina);
                    },
                    TokenKind::Mul => {
                        let rhs = self.parse_expr()?;
                        let bina = Expr::binary(Op::Mul, expr.unwrap(), rhs);
                        return Ok(bina);
                    },
                    TokenKind::Div => {
                        let rhs = self.parse_expr()?;
                        let bina = Expr::binary(Op::Div, expr.unwrap(), rhs);
                        return Ok(bina);
                    },
                    _ => return Err(format!("Unexpected token: {}", token.text)),
                }
            }
            Some(Err(e)) => return Err(e.to_string()),
            None => return Err("Unexpected none token".to_string()),
        }
        expr
    }

    pub fn parse(&mut self) -> Result<Code, String> {
        let mut stmts = Vec::new();
        while let Ok(stmt) = self.parse_stmt() {
            match stmt {
                Stmt::EOF => break,
                _ => stmts.push(stmt),
            }
        }
        Ok(Code { stmts })
    }
}

impl Stmt {
    pub fn by(s: &str) -> Self {
        let mut lexer = Lexer::new(s);
        let mut parser = Parser::new(&mut lexer);
        let stmt = parser.parse_stmt();
        if stmt.is_err() {
            panic!("解析错误: {}", stmt.err().unwrap());
        }
        stmt.unwrap()
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    fn case(input: &str, expected: Stmt) {
        let mut lexer = Lexer::new(input);
        let mut parser = Parser::new(&mut lexer);
        let stmt = parser.parse_stmt();
        match stmt {
            Ok(stmt) => assert_eq!(stmt, expected),
            Err(e) => assert!(false, "解析错误: {}", e),
        }
    }

    #[test]
    fn test_parse_stmt() {
        case("41", Stmt::by("41"));
        case("41.2", Stmt::by("41.2"));
        case("\"hello\"", Stmt::by("\"hello\""));
        case("true", Stmt::by("true"));
        case("false", Stmt::by("false"));
    }
}
    

